package cn.dac.controller;

import cn.dac.common.DateUtil;
import cn.dac.common.LoginStatusEnum;
import cn.dac.entity.security.SysUser;
import cn.dac.exception.ThrErrorCodeException;
import cn.dac.service.SecurityService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.Collection;
import java.util.Date;
import java.util.List;

@Controller
public class SecurityController extends BaseController {

    @Resource
    private SecurityService securityService;





    //@RequiresRoles("ADMIN")
    @RequestMapping(value="/index",method= RequestMethod.GET)
    public String index(Model model){
        String loginName = (String) SecurityUtils.getSubject().getPrincipal();
        model.addAttribute("loginName", loginName);
        return "index";
    }

    @RequestMapping(value="",method=RequestMethod.GET)
    public String defaultIndex(Model model){
        String loginName = (String) SecurityUtils.getSubject().getPrincipal();
        model.addAttribute("loginName", loginName);
        return "index";
    }

    @RequestMapping(value="/login",method=RequestMethod.GET)
    public String loginForm(HttpServletRequest request,Model model){
        model.addAttribute("user", new SysUser());
        if (isShowCode()){
            request.setAttribute("showVrifyCode",true);
        }
        return "login";
    }
    @RequestMapping(value="/login",method=RequestMethod.POST)
    public String login(HttpServletRequest request, @Valid SysUser user, String vrifyCode,BindingResult bindingResult, RedirectAttributes redirectAttributes){
        if(bindingResult.hasErrors()){
            return "login";
        }

        if (StringUtils.isBlank(user.getLoginName()) || StringUtils.isBlank(user.getPassword())){
            request.setAttribute("message",LoginStatusEnum.DEFAULT.getMessage());
            return "login";
        }
        //获取当前的Subject
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        String trueCode = (String) session.getAttribute("trueCode");
        //验证码
        if (isShowCode()){
            //显示验证码
            request.setAttribute("showVrifyCode",true);
            redirectAttributes.addFlashAttribute("showVrifyCode", true);
            logger.debug("###【检查验证码】sessionCode/submitCode:" +trueCode+"=="+vrifyCode);
            if (vrifyCode==null || !vrifyCode.toLowerCase().equals(trueCode.toLowerCase())){
                request.setAttribute("message",LoginStatusEnum.VERIFICATION_CODE_ERROR.getMessage());
                return "login";
            }
        }

        //CustomFormAuthenticationFilter 过滤器方式
        /*String code = (String)request.getAttribute("code");
        if (code!=null&&LoginStatusEnum.getMessageByCode(code)!=null){
            request.setAttribute("message",LoginStatusEnum.VERIFICATION_CODE_ERROR.getMessage());
            return "login";
        }*/

        //次数太多，15分钟后允许登陆
        String errorLastTime = (String) request.getSession().getAttribute("errorLastTime");
        if(errorLastTime!=null){
            logger.debug("###【错误最后时间】："+errorLastTime);
            String nowTime = DateUtil.parseDateToStr(new Date(), "yyyy-MM-dd HH:mm:ss");
            if (DateUtil.getDiffTime(errorLastTime,nowTime,DateUtil.UNIT_MINUTE)<15){
                request.setAttribute("message",LoginStatusEnum.EXCESSIVE_ATTEMPTS.getMessage());
                return "login";
            }
        }
        //
        String loginName = user.getLoginName();
        logger.info("###【当前登陆信息】登陆名/密码："+loginName+"=="+user.getPassword());
        UsernamePasswordToken token = new UsernamePasswordToken(loginName, user.getPassword());
        if(request.getParameter("rememberMe")!=null){
            token.setRememberMe(true);
        }




        try {
            //在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm执行必须的认证检查
            //每个Realm都能在必要时对提交的AuthenticationTokens作出反应
            //所以这一步在调用login(token)方法时,它会走到MyRealm.doGetAuthenticationInfo()方法中,具体验证方式详见此方法
            logger.debug("###对用户[" + loginName + "]进行登录验证..验证开始");
            subject.login(token);
            logger.debug("###对用户[" + loginName + "]进行登录验证..验证通过");
        }catch(UnknownAccountException uae){
            logger.debug("###对用户[" + loginName + "]进行登录验证..验证未通过,未知账户");
            redirectAttributes.addFlashAttribute("message", LoginStatusEnum.UNKNOWN_ACCOUNT.getMessage());
        }catch(IncorrectCredentialsException ice){
            logger.debug("###对用户[" + loginName + "]进行登录验证..验证未通过,错误的凭证");
            redirectAttributes.addFlashAttribute("message", LoginStatusEnum.INCORRECT_CREDENTIALS.getMessage());
        }catch(LockedAccountException lae){
            logger.debug("###对用户[" + loginName + "]进行登录验证..验证未通过,账户已锁定");
            redirectAttributes.addFlashAttribute("message", LoginStatusEnum.ACCOUNT_LOCKED.getMessage());
        }catch(ExcessiveAttemptsException eae){
            logger.debug("###对用户[" + loginName + "]进行登录验证..验证未通过,错误次数过多");
            redirectAttributes.addFlashAttribute("message", LoginStatusEnum.EXCESSIVE_ATTEMPTS.getMessage());
            request.getSession().setAttribute("errorLastTime", DateUtil.parseDateToStr(new Date(), "yyyy-MM-dd HH:mm:ss"));
        }catch (ThrErrorCodeException tece){
            logger.debug("###对用户[" + loginName + "]进行登录验证..显示验证码");
            redirectAttributes.addFlashAttribute("message", LoginStatusEnum.INCORRECT_CREDENTIALS.getMessage());
        }catch(AuthenticationException ae){
            //通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景
            logger.debug("###对用户[" + loginName + "]进行登录验证..验证未通过,堆栈轨迹如下");
            ae.printStackTrace();
            redirectAttributes.addFlashAttribute("message", LoginStatusEnum.DEFAULT.getMessage());
        }
        //验证是否登录成功
        if(subject.isAuthenticated()){
            logger.debug("###用户[" + loginName + "]登录认证通过(这里可以进行一些认证通过后的一些系统参数初始化操作)");
            return "redirect:/index";
        }else{
            token.clear();
            return "redirect:/login";
        }
    }

    @RequestMapping(value="/logout",method=RequestMethod.GET)
    public String logout(RedirectAttributes redirectAttributes ){

        //删除用户session缓存
        securityService.removeOldUserSession();

        //使用权限管理工具进行用户的退出，跳出登录，给出提示信息
        SecurityUtils.getSubject().logout();


        redirectAttributes.addFlashAttribute("message", "您已安全退出");
        return "redirect:/login";
    }

    @RequestMapping("/pages/403")
    public String unauthorizedRole(){
        logger.info("###【没有权限】");
        return "pages/403";
    }

    @RequestMapping("/kickout")
    public String kickout(){
        //根据业务需要 可配置返回json
        return "kickout";
    }
    @RequestMapping("/online")
    @ResponseBody
    public String online(){
        //logger.debug(sessionDAO.getActiveSessions()+"");
        return "/user/online";
    }


    private boolean isShowCode(){
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        String retryCount = session.getAttribute("RETRY_COUNT")==null?"0":session.getAttribute("RETRY_COUNT").toString();
        String trueCode = (String) session.getAttribute("trueCode");

        return (Integer.valueOf(retryCount)>0||StringUtils.isNotEmpty(trueCode));
    }
}
